/**
 * Copyright 2009 Takahiko Ito
 *
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.unigram.oluolu.rqe.postprocessor;

import org.apache.hadoop.conf.Configuration;

/**
 * An implementation of IPostProcessor. This class is extended 
 * NgramPostProcessor. This object validates whether input related 
 * query pairs are mistaken query and the correction query.  
 */
public class SpellCheckPostProcessor
    extends NgramPostProcessor {

    /**
     * Constructor.
     * 
     * @param conf configuration
     */
    public SpellCheckPostProcessor(final Configuration conf) {
        super(conf);
        this.likelihoodThredhold = conf.getFloat(LIKELIHOOD_THRESHOLD, 
                DEFAULT_LIKLIHOOD_THRESHOLD);
    }
    
    /**
     * Validate whether the query pair should be added to dictionary.
     * 
     * @param firstFreq frequency of first query
     * @param secondFreq  frequency of second query
     * @param pairFreq frequency of the occurrence of first query and 
     *         second query in the same sessions
     * @param totalFreq the number of query in the input data
     * @return true when the query is added to a dictionary 
     *          otherwise returns false
     */
    @Override
    protected final boolean validate(final Long firstFreq,
            final Long secondFreq, final Long pairFreq, 
            final Long totalFreq) {

        if (firstFreq > secondFreq) {
            return false;
        }
        
        if (likelihoodRatio(firstFreq, secondFreq, 
                pairFreq, totalFreq)  > this.likelihoodThredhold) {
            return true;
        } 
        return false;

    }
    
    // TODO create test cases
    /**
     * Validate query pair based on log likelihood ratio.
     * 
     * @param firstFreq frequency of first query
     * @param secondFreq  frequency of second query
     * @param pairFreq frequency of the occurrence of first query and 
     *         second query in the same sessions
     * @param totalFreq the number of query in the input data
     * @return log likelihood ratio
     */
    private static double likelihoodRatio(final Long firstFreq,
            final Long secondFreq, final Long pairFreq, 
            final Long totalFreq) {

        double p  = (double) secondFreq / (double) totalFreq;
        double p1 = (double) pairFreq / (double) firstFreq;
        double p2 = (double) (secondFreq - pairFreq) 
                        / (double) (totalFreq - firstFreq);
        
        double l1 = Math.log(likelihood(pairFreq, firstFreq, p));
        double l2 = Math.log(likelihood((secondFreq-pairFreq), 
                (totalFreq - firstFreq), p));
        double l3 = Math.log(likelihood(pairFreq, firstFreq, p1));
        double l4 = Math.log(likelihood((secondFreq - pairFreq), 
                (totalFreq - firstFreq), p2));
        
        return -2 * (l1 + l2 - l3 - l4); 
    }
    
    /**
     * Calculate likelihood.
     * 
     * @param k the number of occurrence
     * @param n the number of try
     * @param x probability
     * @return likelihood
     */
    private static double likelihood(final long k, 
            final long n, final double x) {
        double result = 1.0;
        return Math.pow(x, k) * Math.pow((1-x), (n-k));
    }

    /** threshold of likelihood ratio. */
    private final double likelihoodThredhold;
    
    /** symbol: likelihood threshold. */
    public static final String LIKELIHOOD_THRESHOLD
        = "oluolu.rqe.likelihood.threshold";    
    
    /** default: threshold for likelihood rate. */
    public static final float DEFAULT_LIKLIHOOD_THRESHOLD = 1.5F;  
    
}
